import { _decorator, Color, Component, ERaycast2DType, EventTouch, Graphics, instantiate, Intersection2D, log, Node, PhysicsSystem2D, PolygonCollider2D, v2, Vec2, warn } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Cut')
export class Cut extends Component {
    // 用来切割的本体节点
    @property(Node)
    baseNode: Node = null!;
    // 画线工具(模拟小刀)
    @property(Graphics)
    cutGraphics: Graphics;

    onLoad () {
        // 绘制用来切割的本体
        this.drawCollider(this.baseNode);
        // 注册监听触摸事件
        this.registerEvent();
    }

    /**
     * 根据物理包围盒的 points 填充绘图
     * @param box 注意要使用多边形碰撞盒
     * @param isNewColor 是否随机颜色
     */
    private drawCollider (box: Node, isNewColor: boolean = false) {
        const points = box.getComponent(PolygonCollider2D).points;
        const ctx = box.getComponent(Graphics);
        ctx.clear();
        const len = points.length;
        ctx.moveTo(points[len - 1].x, points[len - 1].y);
        for (let i = 0; i < points.length; i++) {
            ctx.lineTo(points[i].x, points[i].y);
        }
        // 是否随机一个颜色
        if (isNewColor) {
            ctx.fillColor = new Color(Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255));
        }
        ctx.fill();
    }

    /**
     * 注册监听触摸事件
     */
    private registerEvent () {
        this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
            this.cutGraphics.clear();
            const startPoint = e.getUIStartLocation();
            const movePoint = e.getUILocation();
            this.cutGraphics.moveTo(startPoint.x, startPoint.y);
            this.cutGraphics.lineTo(movePoint.x, movePoint.y);
            this.cutGraphics.stroke();
        }, this);
        this.node.on(Node.EventType.TOUCH_END, (e: EventTouch) => {
            this.cutGraphics.clear();
            const startPoint = e.getUIStartLocation();
            const endPoint = e.getUILocation();
            log(`画线起始点：${startPoint}，画线终点：${endPoint}`);
            // 检测两个画线点与碰撞体相交
            this.checkPoints(startPoint, endPoint);
        }, this);
    }

    /**
     * 检测两个画线点与碰撞体相交
     */
    private checkPoints(point1, point2) {
        // 射线检测 （Closest：检测射线路径上最近的碰撞体）
        const result1 = PhysicsSystem2D.instance.raycast(point1, point2, ERaycast2DType.Closest);
        // 获取所有的交点，需要正反来两次射线检测
        const result2 = PhysicsSystem2D.instance.raycast(point2, point1, ERaycast2DType.Closest);
        if (result1.length === 0 || result2.length === 0) {
            warn('无碰撞体');
            return;
        }
        if (result1[0].collider !== result2[0].collider) {
            warn('不是单独碰撞体');
            return;
        }
        if (!(result1[0].collider instanceof PolygonCollider2D)) {
            warn('非多边形物理碰撞盒无points属性');
            return;
        }
        // 与射线相交的碰撞体(单独)
        const baseCollider: PolygonCollider2D = result1[0].collider;
        let jointPoint1: Vec2 = v2(0, 0);
        let jointPoint2: Vec2 = v2(0, 0);
        // 射线与碰撞体相交的点 转换为 刚体本地坐标系下的点
        baseCollider.body.getLocalPoint(result1[0].point, jointPoint1);
        baseCollider.body.getLocalPoint(result2[0].point, jointPoint2);
        // 把碰撞体的顶点以两个交点为界限分割为两部分
        this.cutCollider(baseCollider, jointPoint1, jointPoint2);
    }

    /**
     * 把碰撞体的顶点以两个交点为界限分割为两部分
     * @param baseCollider 碰撞体
     * @param jointPoint1 射线与碰撞体相交点1 
     * @param jointPoint2 射线与碰撞体相交点2
     */
    private cutCollider(baseCollider: PolygonCollider2D, jointPoint1: Vec2, jointPoint2: Vec2) {
        // 多边形顶点数组
        const points = baseCollider.points;
        log("points：", points);
        // 获取相交点在碰撞体边的下标
        let index1 = undefined;
        let index2 = undefined;
        for (let i = 0; i < points.length; i++) {
            let p1 = points[i];
            let p2 = i === points.length - 1 ? points[0] : points[i + 1];
            // 计算点到直线的距离（近似判断点在线上,记录交点边的下标） pointLineDistance(point,lineStart,lineEnd,isSegment)
            if (Intersection2D.pointLineDistance(jointPoint1, p1, p2, true) < 1) {
                index1 = i;
            }
            if (Intersection2D.pointLineDistance(jointPoint2, p1, p2, true) < 1) {
                index2 = i;
            }
            if (index1 !== undefined && index2 !== undefined) {
                break;
            }
        }
        log(`相交点1的边下标${index1}，交点2的边下标${index2}`);
        // 根据此次分割的位置，把顶点装入两个数组
        const cutPoints1: Vec2[] = [];
        const cutPoints2: Vec2[] = [];
        let cutFlag = true;
        for (let i = 0; i < points.length; i++) {
            let temp = points[i].clone();
            if (cutFlag) {
                cutPoints1.push(temp);
            } else {
                cutPoints2.push(temp);
            }
            if (i === index1 || i === index2) {
                cutPoints1.push(i === index1 ? jointPoint1.clone() : jointPoint2.clone());
                cutPoints2.push(i === index1 ? jointPoint1.clone() : jointPoint2.clone());
                cutFlag = !cutFlag;
            }
        }
        log("分割后的顶点", cutPoints1, cutPoints2);
        // 重新设置碰撞体顶点
        this.reSetCollider(baseCollider, cutPoints1, cutPoints2);
    }

    /**
     * 重新设置碰撞体
     * @param baseCollider 本体
     * @param basePoints 本体新顶点
     * @param cutPoints  切割体顶点
     */
    private reSetCollider(baseCollider: PolygonCollider2D, basePoints: Vec2[], cutPoints: Vec2[]) {
        // 设置本体碰撞体顶点
        baseCollider.points = basePoints;
        baseCollider.apply();
        // 重新绘制本体
        this.drawCollider(baseCollider.node);
        // 克隆一个本体作为切下来的碰撞体
        const cloneNode = instantiate(baseCollider.node);
        this.node.addChild(cloneNode);
        const cutCollider = cloneNode.getComponent(PolygonCollider2D);
        cutCollider.points = cutPoints;
        cutCollider.apply();
        // 绘制第二个，随机颜色
        this.drawCollider(cloneNode, true);
    }
}


